]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st: Use scaled surfaces for creating cairo background shadows
authorMarco Trevisan (Treviño) <mail@3v1n0.net>
Mon, 25 Feb 2019 12:45:02 +0000 (13:45 +0100)
committerJonas Ådahl <jadahl@gmail.com>
Fri, 1 Mar 2019 17:12:52 +0000 (17:12 +0000)
Create the surfaces for background shadows at scaled sizes and then draw on them
using logical coordinates, by setting the surface device scale accordingly.

Use the said surface scale when generating the actual shadow cairo pattern
but in such case, to reduce the number of code changes, is better to work in
absolute coordinates, and to do so:
  1) Create a temporary shadow-spec copy with scaled values to absolute sizes
  2) Invert the scaling on the shadow matrix
  3) Do the actual painting in absolute coordinates
  4) Set the shadow matrix scaling back to the logical coordinates.

Finally scale down the created shadow pattern surface size when painting it,
applying again a reverse scale to the matrix.

https://bugzilla.gnome.org/show_bug.cgi?id=765011
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5

src/st/st-private.c
src/st/st-theme-node-drawing.c

index 688b17b785151af9a2c0c2387628cdc1c8386d14..a74368709cfdefe3bedf7a115bb7806d4c3f3f76 100644 (file)
@@ -536,9 +536,10 @@ _st_create_shadow_pipeline_from_actor (StShadow     *shadow_spec,
  * the offset.
  */
 cairo_pattern_t *
-_st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
+_st_create_shadow_cairo_pattern (StShadow        *shadow_spec_in,
                                  cairo_pattern_t *src_pattern)
 {
+  g_autoptr(StShadow) shadow_spec = NULL;
   static cairo_user_data_key_t shadow_pattern_user_data;
   cairo_t *cr;
   cairo_surface_t *src_surface;
@@ -549,9 +550,10 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
   gint             width_in, height_in, rowstride_in;
   gint             width_out, height_out, rowstride_out;
   cairo_matrix_t   shadow_matrix;
+  double           xscale_in, yscale_in;
   int i, j;
 
-  g_return_val_if_fail (shadow_spec != NULL, NULL);
+  g_return_val_if_fail (shadow_spec_in != NULL, NULL);
   g_return_val_if_fail (src_pattern != NULL, NULL);
 
   if (cairo_pattern_get_surface (src_pattern, &src_surface) != CAIRO_STATUS_SUCCESS)
@@ -564,6 +566,25 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
   width_in  = cairo_image_surface_get_width  (src_surface);
   height_in = cairo_image_surface_get_height (src_surface);
 
+  cairo_surface_get_device_scale (src_surface, &xscale_in, &yscale_in);
+
+  if (xscale_in != 1.0 || yscale_in != 1.0)
+    {
+      /* Scale the shadow specifications in a temporary copy so that
+       * we can work everywhere in absolute surface coordinates */
+      double scale = (xscale_in + yscale_in) / 2.0;
+      shadow_spec = st_shadow_new (&shadow_spec_in->color,
+                                   shadow_spec_in->xoffset * xscale_in,
+                                   shadow_spec_in->yoffset * yscale_in,
+                                   shadow_spec_in->blur * scale,
+                                   shadow_spec_in->spread * scale,
+                                   shadow_spec_in->inset);
+    }
+  else
+    {
+      shadow_spec = st_shadow_ref (shadow_spec_in);
+    }
+
   /* We want the output to be a color agnostic alpha mask,
    * so we need to strip the color channels from the input
    */
@@ -606,6 +627,7 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
                                                      width_out,
                                                      height_out,
                                                      rowstride_out);
+  cairo_surface_set_device_scale (surface_out, xscale_in, yscale_in);
   cairo_surface_set_user_data (surface_out, &shadow_pattern_user_data,
                                pixels_out, (cairo_destroy_func_t) g_free);
 
@@ -616,6 +638,9 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
 
   if (shadow_spec->inset)
     {
+      /* Scale the matrix in surface absolute coordinates */
+      cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in);
+
       /* For inset shadows, offsets and spread radius have already been
        * applied to the original pattern, so all left to do is shift the
        * blurred image left, so that it aligns centered under the
@@ -624,6 +649,10 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
       cairo_matrix_translate (&shadow_matrix,
                               (width_out - width_in) / 2.0,
                               (height_out - height_in) / 2.0);
+
+      /* Scale back the matrix in original coordinates */
+      cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in);
+
       cairo_pattern_set_matrix (dst_pattern, &shadow_matrix);
       return dst_pattern;
     }
@@ -636,6 +665,9 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
   /* 6. Invert the matrix back */
   cairo_matrix_invert (&shadow_matrix);
 
+  /* Scale the matrix in surface absolute coordinates */
+  cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in);
+
   /* 5. Adjust based on specified offsets */
   cairo_matrix_translate (&shadow_matrix,
                           shadow_spec->xoffset,
@@ -657,6 +689,9 @@ _st_create_shadow_cairo_pattern (StShadow        *shadow_spec,
                           - (width_out - width_in) / 2.0,
                           - (height_out - height_in) / 2.0);
 
+  /* Scale back the matrix in scaled coordinates */
+  cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in);
+
   /* 1. Invert the matrix so we can work with it in pattern space
    */
   cairo_matrix_invert (&shadow_matrix);
index a94253f4edfbbf9a1c94a6fa23af0a09da698237..7db928a0d75dc3ea2af60ed4d7e25c489bd6c2b6 100644 (file)
@@ -759,6 +759,7 @@ paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec,
     {
       cairo_surface_t *surface;
       int width, height;
+      double xscale, yscale;
       cairo_matrix_t matrix;
 
       cairo_save (cr);
@@ -775,11 +776,13 @@ paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec,
         /* Something went wrong previously */
         goto no_surface;
 
+      cairo_surface_get_device_scale (surface, &xscale, &yscale);
       width = cairo_image_surface_get_width  (surface);
       height = cairo_image_surface_get_height (surface);
 
       cairo_pattern_get_matrix (pattern, &matrix);
       cairo_matrix_invert (&matrix);
+      cairo_matrix_scale (&matrix, 1.0 / xscale, 1.0 / yscale);
       cairo_transform (cr, &matrix);
 
       cairo_rectangle (cr, 0, height, width, - height);
@@ -803,7 +806,8 @@ paint_background_image_shadow_to_cairo_context (StThemeNode     *node,
                                                 int              x,
                                                 int              y,
                                                 int              width,
-                                                int              height)
+                                                int              height,
+                                                float            resource_scale)
 {
   cairo_pattern_t *shadow_pattern;
 
@@ -819,7 +823,10 @@ paint_background_image_shadow_to_cairo_context (StThemeNode     *node,
       /* Prerender the pattern to a temporary surface,
        * so it's properly clipped before we create a shadow from it
        */
+      width = ceilf (width * resource_scale);
+      height = ceilf (height * resource_scale);
       clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+      cairo_surface_set_device_scale (clipped_surface, resource_scale, resource_scale);
       temp_cr = cairo_create (clipped_surface);
 
       cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR);
@@ -883,6 +890,7 @@ path_extents (cairo_path_t *path,
 static void
 paint_inset_box_shadow_to_cairo_context (StThemeNode     *node,
                                          StShadow        *shadow_spec,
+                                         float            resource_scale,
                                          cairo_t         *cr,
                                          cairo_path_t    *shadow_outline)
 {
@@ -923,8 +931,8 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode     *node,
       /* Bounds of temporary surface */
       int surface_x = floor (shrunk_extents_x1);
       int surface_y = floor (shrunk_extents_y1);
-      int surface_width = ceil (shrunk_extents_x2) - surface_x;
-      int surface_height = ceil (shrunk_extents_y2) - surface_y;
+      int surface_width = ceil ((shrunk_extents_x2 - surface_x) * resource_scale);
+      int surface_height = ceil ((shrunk_extents_y2 - surface_y) * resource_scale);
 
       /* Center of the original path */
       double x_center = (extents_x1 + extents_x2) / 2;
@@ -935,6 +943,7 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode     *node,
       cairo_matrix_t matrix;
 
       shadow_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, surface_width, surface_height);
+      cairo_surface_set_device_scale (shadow_surface, resource_scale, resource_scale);
       temp_cr = cairo_create (shadow_surface);
 
       /* Match the coordinates in the temporary context to the parent context */
@@ -1284,7 +1293,8 @@ st_theme_node_prerender_background (StThemeNode *node,
                                                       has_visible_outline?  outline_path : NULL,
                                                       actor_box.x1,
                                                       actor_box.y1,
-                                                      width, height);
+                                                      width, height,
+                                                      resource_scale);
       cairo_append_path (cr, outline_path);
     }
 
@@ -1301,6 +1311,7 @@ st_theme_node_prerender_background (StThemeNode *node,
     {
       paint_inset_box_shadow_to_cairo_context (node,
                                                box_shadow_spec,
+                                               resource_scale,
                                                cr,
                                                interior_path ? interior_path
                                                              : outline_path);